package cn.thens.kdroid.core.io

import android.content.SharedPreferences
import android.util.LruCache
import io.reactivex.Completable
import io.reactivex.Single
import java.io.File

abstract class Warehouse<K, V> {
    abstract fun pick(key: K): Ware<V>

    fun cache(cache: Warehouse<K, V>): Warehouse<K, V> {
        val source = this
        return object : Warehouse<K, V>() {
            override fun pick(key: K): Ware<V> {
                return source.pick(key).cache(cache.pick(key))
            }
        }
    }

    fun <U> codecValue(codec: Codec<V, U>): Warehouse<K, U> {
        val source = this
        return object : Warehouse<K, U>() {
            override fun pick(key: K): Ware<U> {
                return source.pick(key).codec(codec)
            }
        }
    }

    fun <L> liftKey(func: (L) -> K): Warehouse<L, V> {
        val source = this
        return object : Warehouse<L, V>() {
            override fun pick(key: L): Ware<V> {
                return source.pick(func.invoke(key))
            }
        }
    }

    companion object {

        fun file(): Warehouse<File, ByteArray> {
            return object : Warehouse<File, ByteArray>() {
                override fun pick(key: File): Ware<ByteArray> {
                    return object : Ware<ByteArray>() {
                        override fun get(): Single<ByteArray> {
                            return Single.create { emitter -> emitter.onSuccess(Files.readBytes(key)) }
                        }

                        override fun put(value: ByteArray): Completable {
                            return Completable.create { emitter ->
                                Files.writeBytes(key, value)
                                emitter.onComplete()
                            }
                        }
                    }
                }
            }
        }

        fun <K, V> from(cache: LruCache<K, V>): Warehouse<K, V> {
            return object : Warehouse<K, V>() {
                override fun pick(key: K): Ware<V> {
                    return object : Ware<V>() {
                        override fun get(): Single<V> {
                            return Single.create { emitter ->
                                val value = cache.get(key)
                                if (value != null) {
                                    emitter.onSuccess(value)
                                } else {
                                    emitter.onError(NotFoundException())
                                }
                            }
                        }

                        override fun put(value: V): Completable {
                            return Completable.create { emitter ->
                                cache.put(key, value)
                                emitter.onComplete()
                            }
                        }
                    }
                }
            }
        }

        fun from(prefs: SharedPreferences): Warehouse<String, String> {
            return object : Warehouse<String, String>() {
                override fun pick(key: String): Ware<String> {
                    return object : Ware<String>() {
                        override fun get(): Single<String> {
                            return Single.create { emitter ->
                                if (prefs.contains(key)) {
                                    emitter.onSuccess(prefs.getString(key, "")!!)
                                } else {
                                    emitter.onError(NotFoundException())
                                }
                            }
                        }

                        override fun put(value: String): Completable {
                            return Completable.create { emitter ->
                                prefs.edit().putString(key, value).apply()
                                emitter.onComplete()
                            }
                        }
                    }
                }
            }
        }
    }
}
